package network;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

/**
 * Creared with IntelliJ IDEA.
 * Description:udp服务器
 * User:yxd
 * Date:2022-04-11
 * Time:14:55
 */
/*
站在服务端的角度上:五元组
源IP:服务器程序本机的IP
源端口:服务器自己绑定的端口
目的IP:包含在收到的数据报中(客户端的IP)
目的端口:包含在收到的数据报中(客户端的端口)
协议类型:UDP
 */
public class UDPEchoServer {
    //首先需要有一个DataframSocket实例,这是进行网络编程的前提
    private DatagramSocket socket = null;
    //其次在构造函数里面完成对象的构造,构造socket对象的时候需要显式的绑定一个端口号,这个端口号就是用来区分一个应用程序的,主机收到网卡上的数据时,应该给哪个程序就得明确

    public UDPEchoServer(int port) throws SocketException {
        //这里有这个异常表示socket对象有可能创建失败:1.端口号已经被占用了,同一主机的两个程序不能有相同的端口号,但是一个进程可以绑定多个端口(人和手机号),一个进程创建多个socket对象,每个对象都有自己的端口号这是可以的
        // 2.每个进程能打开的文件个数是有上限的,如果在这之前已经打开了很多文件,那么这个socket文件很有可能打不开
        socket = new DatagramSocket(port);
    }

    public void start() throws IOException {
        System.out.println("启动服务器!");
        //由于UDP不需要建立连接,因此可以直接接收从客户端传来的数据
        while(true){
            //先接收客户端的请求,因为服务器是被动接收数据的,因此开始都是先接收,而不是发送
            DatagramPacket requestPacket = new DatagramPacket(new byte[1024],1024);//需要指定大小
            //这里的receive如果客户端没有发送请求的话,会一直在阻塞状态,直到客户端请求发送才会接收
            socket.receive(requestPacket);//想要接收数据,就需要有一个空的DatagramPacket对象,来让这个方法填充数据
            //此时已经将接收到的数据放在requestPacket里面了,然后将这里面的数据解析出来,解析成字符串
            String request = new String(requestPacket.getData(),0,requestPacket.getLength(),"UTF-8");//也可以指定字符编码,在后面,这里没写
            //然后根据请求计算其所能得到的响应,存放在另一个字符串内
            String response = prcoess(request);
            //然后就要把服务器响应的数据发送回到客户端去
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length/*这个length就不能直接是字符串的长度了,因为这里是字节的长度,而字符串的长度代表的是字符的长度是不一样的*/
            ,requestPacket.getSocketAddress());//这里不仅要放回响应内容,而且要指定好要发送到的IP地址和端口号,这样才可以正确发送,要不然是没有办法准确发送的
            socket.send(responsePacket);//发送过来的是DatagramPacket数据报,返回的也就是这样的数据报,但是发送回去的就不再是一个空的了,而是应该把从上面所得到的响应字符串放到里面然后再发送
            //可以再手动输出一下客户端的IP地址和端口号
            System.out.printf("[%s:%d] req: %s, resp: %s\n",requestPacket.getAddress().toString(),requestPacket.getPort(),request,response);
        }
    }
    //由于这里是回显服务,因此不需要做出改变,其实对于一个服务器来说最难的地方也就是计算响应了
    public String prcoess(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        UDPEchoServer udpEchoServer = new UDPEchoServer(9090);
        udpEchoServer.start();
    }
}
